/*
Copyright (C) 2015 Marien Raat

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <random>
#include <iostream>
#include <functional>

// The map array is a one dimensional array used to describe a two
// dimensional array. The array is layed out with the rows next to
// each other, so (x, y) kan be accesed on the index x + y * width.
// To ease the use of this array, the simple macro TO_ARRAY_INDEX
// is provided.
#define TO_ARRAY_INDEX(x, y, width) (x + y * width)

enum DiamondSquareMapType {
    MAPTYPE_LAND,
    MAPTYPE_ISLAND,
    MAPTYPE_ARCHIPELAGO
};

struct DiamondSquareMapSettings{
    int size; // Size of the square, should be a power of 2 plus 1
    double h; // The displacement will be decreased by 2 ^ (- h) each pass
              // A high h-value will result in a smooth map and a low h-value
              // will result in a rough maps
    DiamondSquareMapType mapType;
    bool overwriteOldMap; // If false, new space will be allocated in the
                          // heap for the new map.
                          // The user of the class is responsible for cleaning
                          // up all maps and repeatedly running with this as
                          // false will leak memory.
    double (*modifierFunction)(int, double);
};

struct PassSettings {
    double *map; // Map to run the pass on
    double displacementModifier; // Displacement from drand() is multiplied
                                 // by this value. Given by 2 ^ (- (h * pass))
    int passSize; // Length of the sides of the square
    int mapSize; // Size of each side of the map
};

class HeightmapGenerator {
public:
    HeightmapGenerator(std::mt19937 *mt);
    ~HeightmapGenerator();
    void createNewMap(DiamondSquareMapSettings settings);
    void pass(); // Apply the next pass to the map if it is made and
                 // it is not done
    void finishMap(); // Calls pass() until isMapDone
    bool isMapDone();

    PassSettings currentPassSettings;

    double *map;
private:

    DiamondSquareMapSettings currentMapSettings;
    bool mapInitialized, firstMap;

    std::mt19937 *mt;
    std::normal_distribution<double> *stdDist;

    void passDiamondSquare(PassSettings *settings);
    void diamond(int x, int y, PassSettings *settings);
    void square(int x, int y, PassSettings *settings);
    bool isValid(int x, int y, int size);
    double drand(); // Returns a random double between -1.0 and 1.0
                    // using the given mt and the stdDist
    void fillSidesWith(double *map, double cornerValue, int size);
};

// Simple functions to use as modifier functions
double logaritmicDisplacementModifier(int pass, double h);
double customDisplacementModifier(int pass, double h);
